package com.atguigu.service.impl;

import com.atguigu.cache.ShopCache;
import com.atguigu.constant.RedisConst;
import com.atguigu.entity.ProductSalePropertyKey;
import com.atguigu.entity.SkuImage;
import com.atguigu.entity.SkuInfo;
import com.atguigu.exception.SleepUtils;
import com.atguigu.mapper.ProductSalePropertyKeyMapper;
import com.atguigu.mapper.SkuSalePropertyValueMapper;
import com.atguigu.service.ProductSalePropertyKeyService;
import com.atguigu.service.SkuDetailService;
import com.atguigu.service.SkuImageService;
import com.atguigu.service.SkuInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA2020.
 * User: yangchao
 * Date: 2021/11/20:20:12
 * Description:
 */
@Service
public class SkuDetailServiceImpl implements SkuDetailService {
    @Autowired
    private SkuInfoService skuInfoService;
    @Autowired
    private SkuImageService skuImageService;
    @Autowired
    private ProductSalePropertyKeyMapper salePropertyKeyMapper;
    @Autowired
    private SkuSalePropertyValueMapper skuSalePropertyValueMapper;
    @Resource
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    @ShopCache(prefix = "sku:info")
    @Override
    public SkuInfo getSkuInfo(Long skuId) {

        SkuInfo skuInfo = getSkuInfoFromDB(skuId);

//        getSkuInfoFromRedisson(skuId);
        return skuInfo;
    }

    //表示使用redis set+lua 脚本实现的分布式锁！
//    private SkuInfo getSkuInfoFromRedis(long skuId) {
//        String skuKey = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKUKEY_SUFFIX;
//        // 获取缓存的数据
//        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(skuKey);
//        if(skuInfo==null){
//            //在从数据库获取数据的时候，我们需要添加一个锁，为了保证在高并发情况下防止缓存击穿。
//            String lockKey = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKULOCK_SUFFIX;
//            // 设置UUID
//            String uuid = UUID.randomUUID().toString();
//            boolean acquireLock = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, RedisConst.WAITTIN_GET_LOCK_TIME, TimeUnit.SECONDS);
//            if (acquireLock){
//                skuInfo = getSkuInfoFromDB(skuId);  //查数据
//                // 缓存穿透！
//                if (skuInfo==null){
//                    // 设置一个空对象进去,这个数据应该给一个短暂的过期时间
//                    SkuInfo emptySkuInfo = new SkuInfo();
//                    redisTemplate.opsForValue().set(skuKey,emptySkuInfo,RedisConst.SKUKEY_TEMPORARY_TIMEOUT,TimeUnit.SECONDS);
//                    // 返回不存在的对象
//                    return emptySkuInfo;
//                }
//                // 将从数据库查询到数据放入缓存 商品详情页面数据，应该有个过期时间
//                redisTemplate.opsForValue().set(skuKey,skuInfo, RedisConst.SKUKEY_TIMEOUT, TimeUnit.SECONDS);
//                // 获取完成之后：删除对应的锁！ 使用lua 脚本
//                // 这个脚本只在客户端传入的值和键的口令串相匹配时
//                String script="if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
//                // 执行这个lua 脚本
//                DefaultRedisScript defaultRedisScript = new DefaultRedisScript();
//                // 将lua脚本放入对象
//                defaultRedisScript.setScriptText(script);
//                // 设置一个返回值
//                defaultRedisScript.setResultType(Long.class);
//                redisTemplate.execute(defaultRedisScript, Arrays.asList(lockKey),uuid);
//                // 返回真正的数据
//                return skuInfo;
//            }else {
//                SleepUtils.sleep(1);
//                // 继续获取数据
//                return getSkuInfo(skuId);
//            }
//        }else{
//            return skuInfo;
//        }
//    }

//    private SkuInfo getSkuInfoFromRedisson(Long skuId) {
//        //定义一个缓存key
//        String skuKey = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKUKEY_SUFFIX;
//        // 从缓存获取数据
//        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(skuKey);
//        //如果缓存中没有数据
//        if(skuInfo==null){
//            //缓存中没有数据先获取锁 然后把数据放入缓存
//            String lockKey = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKULOCK_SUFFIX;
//            RLock lock = redissonClient.getLock(lockKey);
//            try {
//                boolean acquireLock = lock.tryLock(RedisConst.WAITTIN_GET_LOCK_TIME, RedisConst.LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
//                if(acquireLock){
//                    skuInfo = getSkuInfoFromDB(skuId);
//                    //防止缓存穿透---到时候再细看
//                    if(skuInfo==null){
//                        // 设置一个空对象进去,这个数据应该给一个短暂的过期时间
//                        SkuInfo emptySkuInfo = new SkuInfo();
//                        redisTemplate.opsForValue().set(skuKey,emptySkuInfo,RedisConst.SKUKEY_TEMPORARY_TIMEOUT,TimeUnit.SECONDS);
//                        // 返回不存在的对象
//                        return emptySkuInfo;
//                    }
//                    // 将从数据库查询到数据放入缓存 商品详情页面数据，应该有个过期时间
//                    redisTemplate.opsForValue().set(skuKey,skuInfo,RedisConst.SKUKEY_TIMEOUT,TimeUnit.SECONDS);
//                    return skuInfo;
//                }else{
//                    // 自旋等待
//                    SleepUtils.sleep(1);
//                    return getSkuInfo(skuId);
//                }
//            } catch (Exception e) {
//                e.printStackTrace();
//            } finally {
//                //不管是否有return 都会执行
//                lock.unlock();
//            }
//        }else{
//            //直接返回缓存里面的数据
//            return skuInfo;
//        }
//        //如果以上代码出现问题 用数据库兜底
//        return getSkuInfoFromDB(skuId);
//    }

    @ShopCache(prefix = "saleProperty:info")
    @Override
    public List<ProductSalePropertyKey> getSpuSaleProperty(long productId, long skuId) {
        return salePropertyKeyMapper.getSpuSaleProperty(productId,skuId);
    }

    @Override
    public Map getSkuSalePropertyValueIdMapping(long productId) {

        Map<Object, Object> retMap = new HashMap<>();
        List<Map> valueIdMap=skuSalePropertyValueMapper.getSkuSalePropertyValueIdMapping(productId);
        if (!CollectionUtils.isEmpty(valueIdMap)){
            // 循环遍历
            for (Map skuMap : valueIdMap) {
                retMap.put(skuMap.get("sale_property_value_id"),skuMap.get("sku_id"));
            }
        }
        return retMap;
    }

    private SkuInfo getSkuInfoFromDB(long skuId) {
        SkuInfo skuInfo = skuInfoService.getById(skuId);
        //封装图片进去
        if(skuInfo!=null) {
            QueryWrapper<SkuImage> wrapper = new QueryWrapper<SkuImage>().eq("sku_id", skuId);
            List<SkuImage> skuImageList = skuImageService.list(wrapper);
            skuInfo.setSkuImageList(skuImageList);
        }
        return skuInfo;
    }

}
